﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;

namespace HYGE.AsyncCommand
{
    /// <summary>
    /// IAsyncJob実装用の基底抽象クラス。
    /// </summary>
    internal abstract class AsyncJobBase : IAsyncJob
    {
        #region INotifyPropertyChangedMembers

        #region Events

        #region PropertyChanged
        /// <summary>
        /// INotifyPropertyChanged.PropertyChanged。
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged = null;

        /// <summary>
        /// プロパティが変更された際に実行される。
        /// </summary>
        /// <param elementName="propertyName"></param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = null;
            handler = this.PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        #endregion

        #endregion

        #region IDisposableMembers

        #region Fields

        #region disposed
        /// <summary>
        /// このIAsyncJobがDisposeされたかどうかを表すフラグ。Disposeされた場合に限り、True。
        /// </summary>
        private bool disposed = false;
        #endregion

        #endregion

        #region Methods

        #region ThrowIfDisposed
        /// <summary>
        /// Disposeされている場合に、ObjectDisposedExceptionを投げる。
        /// </summary>
        protected virtual void ThrowIfDisposed()
        {
            if (this.disposed)
                throw (new ObjectDisposedException(this.GetType().Name));
        }
        #endregion

        #region ReleaseManagedResources
        /// <summary>
        /// マネージリソース開放を行う。
        /// </summary>
        protected virtual void ReleaseManagedResources()
        {
        }
        #endregion

        #region ReleaseUnmanagedResources
        /// <summary>
        /// アンマネージリソース開放を行う。
        /// </summary>
        protected virtual void ReleaseUnmanagedResources()
        {
        }
        #endregion

        #region Dispose(bool disposing)
        /// <summary>
        /// このAsyncJobBaseが持つリソースの開放を行う。
        /// </summary>
        /// <param elementName="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (this.disposed)
                return;

            //イベントハンドラ、マネージリソース開放
            if (disposing)
            {
                var handlers = this.PropertyChanged.GetInvocationList().Cast<PropertyChangedEventHandler>();
                foreach (var handler in handlers)
                    this.PropertyChanged -= handler;
                this.ReleaseManagedResources();
            }

            //アンマネージリソース開放
            this.ReleaseUnmanagedResources();

            //Dispose処理終了
            this.disposed = true;
        }
        #endregion

        #region Dispose
        /// <summary>
        /// IDisposable.Dispose()の実装。
        /// </summary>
        /// <param elementName="disposing"></param>
        public void Dispose()
        {
            //Dispose実行
            this.Dispose(true);

            //継承クラスでFinalizeメソッドが呼ばれないよう、SuppressFinalize()を実行。Disposeメソッド内で、Finalizeに相当することを実行するため
            GC.SuppressFinalize(this);
        }
        #endregion

        #endregion

        #endregion

        #region IAsyncJobMembers

        #region Properties

        #region CanBeCanceled
        private bool _CanBeCanceled = false;
        /// <summary>
        /// ジョブをキャンセルできるかどうかを表すフラグ。キャンセルできる場合に限り、True。
        /// IAsyncJob.CanBeCanceledの実装。
        /// </summary>
        public bool CanBeCanceled
        {
            get
            {
                return (this._CanBeCanceled);
            }
            protected set
            {
                if (this._CanBeCanceled == value)
                    return;
                this._CanBeCanceled = value;
                this.OnPropertyChanged("CanBeCanceled");
            }
        }
        #endregion

        #region IsRunning
        private bool _IsRunning = false;
        /// <summary>
        /// ジョブが実行中かどうかを表すフラグ。実行中である場合に限り、True。
        /// IAsyncJob.IsRunningの実装。
        /// </summary>
        public bool IsRunning
        {
            get
            {
                return (this._IsRunning);
            }
            protected set
            {
                if (this._IsRunning == value)
                    return;
                this._IsRunning = value;
                this.OnPropertyChanged("IsRunning");
            }
        }
        #endregion

        #region IsCompleted
        private bool _IsCompleted = false;
        /// <summary>
        /// ジョブが完了したかどうかを表すフラグ。完了した場合に限り、True。
        /// IAsyncJob.IsCompletedの実装。
        /// </summary>
        public bool IsCompleted
        {
            get
            {
                return (this._IsCompleted);
            }
            protected set
            {
                if (this._IsCompleted == value)
                    return;
                this._IsCompleted = value;
                this.OnPropertyChanged("IsCompleted");
            }
        }
        #endregion

        #region IsCanceled
        private bool _IsCanceled = false;
        /// <summary>
        /// ジョブ実行中にキャンセルされたかどうかを表すフラグ。キャンセルされた場合に限り、True。
        /// IAsyncJob.IsCanceledの実装。
        /// </summary>
        public bool IsCanceled
        {
            get
            {
                return (this._IsCanceled);
            }
            protected set
            {
                if (this._IsCanceled == value)
                    return;
                this._IsCanceled = value;
                this.OnPropertyChanged("IsCanceled");
            }
        }
        #endregion

        #region IsFaulted
        private bool _IsFaulted = false;
        /// <summary>
        /// ジョブ実行中に例外が発生したかどうかを表すフラグ。発生した場合に限り、True。
        /// IAsyncJob.IsFaultedの実装。
        /// </summary>
        public bool IsFaulted
        {
            get
            {
                return (this._IsFaulted);
            }
            protected set
            {
                if (this._IsFaulted == value)
                    return;
                this._IsFaulted = value;
                this.OnPropertyChanged("IsFaulted");
            }
        }
        #endregion

        #region Exception
        private AggregateException _Exception = null;
        /// <summary>
        /// ジョブの実行中に発生したAggregateException。
        /// IAsyncJob.Exceptionの実装。
        /// </summary>
        public AggregateException Exception
        {
            get
            {
                return (this._Exception);
            }
            protected set
            {
                if (this._Exception == value)
                    return;
                this._Exception = value;
                this.OnPropertyChanged("Exception");
            }
        }
        #endregion

        #region Description
        /// <summary>
        /// ジョブを説明する文字列。
        /// IAsyncJob.Descriptionの実装。
        /// </summary>
        public string Description
        {
            get;
            private set;
        }
        #endregion

        #region Id
        /// <summary>
        /// ジョブの一意ID。
        /// IAsyncJob.Idの実装。
        /// </summary>
        public int Id
        {
            get;
            protected set;
        }
        #endregion

        #endregion

        #region Methods

        #region Cancel
        /// <summary>
        /// IAsyncJob.Cancel()実装用の抽象メソッド。
        /// </summary>
        public abstract void Cancel();
        #endregion

        #region Start
        /// <summary>
        /// IAsyncJob.Start()実装用の抽象メソッド。
        /// </summary>
        /// <param name="scheduler"></param>
        public abstract void Start(TaskScheduler scheduler = null);
        #endregion

        #endregion

        #endregion

        #region Constructors

        #region Default
        /// <summary>
        /// デフォルトコンストラクタ。
        /// </summary>
        /// <param name="description"></param>
        public AsyncJobBase(string description = "")
        {
            this.Description = description;
        }
        #endregion

        #endregion
    }
}
